#include "SHA512.h"
#include "TestBase.h"
#include <string>

#ifdef WIN32
#include <windows.h>
#else
#include <sys/time.h>
#endif

unsigned long long GetTime()
{
#ifdef WIN32
	LARGE_INTEGER tps, tick, time;
	QueryPerformanceFrequency(&tps);
	QueryPerformanceCounter(&tick);
	time.QuadPart = tick.QuadPart/tps.QuadPart;
	return (unsigned long long)((time.QuadPart * 1000000) + (tick.QuadPart / (tps.QuadPart / 1000000)));
#else
	timeval tv;
	gettimeofday(&tv, NULL);
	return (unsigned long long)((tv.tv_sec * 1000000) + (tv.tv_usec));
#endif
}

TestBase::TestBase()
{
	info.Name = "CryptoSystem Test 2 - SHA512 speed test";
	info.Category = 0x3102;
	info.CategoryName = "CryptoSystem";
	info.File = __FILE__;
}

bool TestBase::Run()
{
#ifdef WIN32
	SetThreadAffinityMask(GetCurrentThread(), 1); // time counters between threads cannot be assured to be in sync, force to 1st core to work around
#endif
	unsigned char output[64];
	const char* buf;
	StateSHA512 state;
	unsigned int i, len;
	unsigned long long s, e;
	unsigned long avg, min, max, step;
	std::string msg;
	std::cout << "** SHA512 speed test **" << std::endl;
	avg = max = step = 0;
	min = (unsigned long long)(0xFFFFFFFF);
	for (i = 0; i < 100; i++)
	{
		s = GetTime();
		SHA512_Init(&state);
		SHA512_Update(&state, NULL, 0);
		SHA512_Final(&state, output);
		e = GetTime();
		step = e - s;
		if (step < min)
		{
			min = step;
		}
		if (step > max)
		{
			max = step;
		}
		avg += step;
	}
	avg /= i;
	printf("Empty message   : %.4dus (avg) %.4dus (min) %.4dus (max)\n", avg, min, max);
	i = 100;
	while (msg.length() < i)
	{
		msg += "a";
	}
	avg = max = step = 0;
	min = (unsigned long long)(0xFFFFFFFF);
	buf = msg.c_str();
	len = msg.length();
	for (i = 0; i < 100; i++)
	{
		s = GetTime();
		SHA512_Init(&state);
		SHA512_Update(&state, buf, len);
		SHA512_Final(&state, output);
		e = GetTime();
		step = e - s;
		if (step < min)
		{
			min = step;
		}
		if (step > max)
		{
			max = step;
		}
		avg += step;
	}
	avg /= i;
	printf("100 characters  : %.4dus (avg) %.4dus (min) %.4dus (max)\n", avg, min, max);
	i = 1000;
	while (msg.length() < i)
	{
		msg += "a";
	}
	avg = max = step = 0;
	min = (unsigned long long)(0xFFFFFFFF);
	buf = msg.c_str();
	len = msg.length();
	for (i = 0; i < 100; i++)
	{
		s = GetTime();
		SHA512_Init(&state);
		SHA512_Update(&state, buf, len);
		SHA512_Final(&state, output);
		e = GetTime();
		step = e - s;
		if (step < min)
		{
			min = step;
		}
		if (step > max)
		{
			max = step;
		}
		avg += step;
	}
	avg /= i;
	printf("1000 characters : %.4dus (avg) %.4dus (min) %.4dus (max)\n", avg, min, max);
	i = 1500;
	while (msg.length() < i)
	{
		msg += "a";
	}
	avg = max = step = 0;
	min = (unsigned long long)(0xFFFFFFFF);
	buf = msg.c_str();
	len = msg.length();
	for (i = 0; i < 100; i++)
	{
		s = GetTime();
		SHA512_Init(&state);
		SHA512_Update(&state, buf, len);
		SHA512_Final(&state, output);
		e = GetTime();
		step = e - s;
		if (step < min)
		{
			min = step;
		}
		if (step > max)
		{
			max = step;
		}
		avg += step;
	}
	avg /= i;
	printf("1500 characters : %.4dus (avg) %.4dus (min) %.4dus (max)\n", avg, min, max);
	i = 10000;
	while (msg.length() < i)
	{
		msg += "a";
	}
	avg = max = step = 0;
	min = (unsigned long long)(0xFFFFFFFF);
	buf = msg.c_str();
	len = msg.length();
	for (i = 0; i < 100; i++)
	{
		s = GetTime();
		SHA512_Init(&state);
		SHA512_Update(&state, buf, len);
		SHA512_Final(&state, output);
		e = GetTime();
		step = e - s;
		if (step < min)
		{
			min = step;
		}
		if (step > max)
		{
			max = step;
		}
		avg += step;
	}
	avg /= i;
	printf("10000 characters: %.4dus (avg) %.4dus (min) %.4dus (max)\n", avg, min, max);
	return true;
}